﻿package
{
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.geom.Rectangle;
	
	public class BarScroller extends MovieClip
	{
		//VARIABLES INTERNAS SETEABLES OBLIGATORIAS.
			
			public var formClip:MovieClip;	
			public var targetClip:MovieClip;
		
		//VARIABLES INTERNAS SETEABLES OPCIONALES.
		
			public var vAlign:String = "right";
			public var hAlign:String = "bottom";
			public var useHand:Boolean = true;
			public var motionFactor:Number = 0.25;
			public var scrollSpeed:Number = 5;
			public var laneScrollSpeed:Number = 20;
			public var scrollImpulse:Number = 5;
		
		//VARIABLES INTERNAS.
		private var box:MovieClip;
		private var contenido:MovieClip;
		private var length:Number;
		private var direction:String;
		private var prevBtn:MovieClip;
		private var nextBtn:MovieClip;
		private var bar:MovieClip;
		private var lane:MovieClip;
		private var scrollDir:int;
		private var targetPos:Number = 0;
		private var minPos:Number;
		private var laneRect:Rectangle;
		private var dragMotionCount:uint;
		private var currSpeed:Number;
		
		//CONSTRUCTOR.
		public function BarScroller()
		{
			
		}
		
		//INIT.
		public function init():void
		{
			//IDENTIFICA ELEMENTOS DEL TARGETCLIP.
			box = targetClip.box;
			contenido = MovieClip(targetClip.getChildByName("contentHolder"));
			
			//PRECALCULA VALORES DE SCROLLING.
			direction = contenido.width > box.width ? "horizontal" : "vertical";
			length = direction == "horizontal" ? box.width : box.height;
			
			//INICIALIZA LOS BOTONES.
			initBtns();
			
			//INICIALIZA LA BARRA Y EL LANE.
			initBarAndLane();
			
			//DETERMINA LOS LIMITES DEL SCROLLING.
			evalLimits();
		}
		
		//MOVIMIENTO.
			
			//EMPIEZA UN SCROLL.
			private function startScroll(evt:MouseEvent):void
			{
				//DETERMINA LA DIRECCION DE SCROLLING.
				scrollDir = evt.target.parent.dir;
				
				//VELOCIDAD TIPO BOTONES.
				currSpeed = scrollSpeed;
				
				//IMPULSO INICIAL.
				retarget(scrollImpulse);
				
				//INICIA EL MOVIMIENTO.
				addEventListener(Event.ENTER_FRAME, movimiento);
				
				//INICIA EL CALCULO DE POSICION.
				addEventListener(Event.ENTER_FRAME, calcPos);
			}
			
			//TERMINA UN SCROLL.
			private function endScroll(evt:MouseEvent):void
			{
				//TERMINA EL CALCULO DE POSICION.
				removeEventListener(Event.ENTER_FRAME, calcPos);
			}
			
			//CALCULO DE POSICION.
			private function calcPos(evt:Event):void
			{
				//CALCULA EL TARGET.
				retarget(currSpeed);
			}
			
			//CALCULA UN NUEVO TARGET.
			private function retarget(value:Number):void
			{
				//CALCULA.
				targetPos -= scrollDir * value;
				
				//CONTIENE.
				targetPos = targetPos < minPos ? minPos : targetPos;
				targetPos = targetPos > 0 ? 0 : targetPos;
			}
			
			//DETERMINA LOS LIMITES DEL SCROLLING.
			private function evalLimits():void
			{
				if (direction == "horizontal")
					minPos = -(contenido.width - length);
				else
					minPos = -(contenido.height - length);
			}
			
			//MOVIMIENTO.
			private function movimiento(evt:Event):void
			{
				//CALCULA LA DIFERENCIA.
				var dif:Number = direction == "horizontal" ? targetPos - contenido.x : targetPos - contenido.y;
				
				//MUEVE.
				if (direction == "horizontal")
					contenido.x += motionFactor * dif;
				else
					contenido.y += motionFactor * dif;
				
				//EVALUA EL CORTE DEL MOVIMIENTO.
				if (Math.abs(dif) < 0.5)
				{
					//ANCLEA.
					if (direction == "horizontal")
						contenido.x = targetPos;
					else
						contenido.y = targetPos;
					
					//CORTA.
					removeEventListener(Event.ENTER_FRAME, movimiento);
				}
				
				//APLICA EL MOVIMIENTO EN LA BARRA.
				copyMotionToBar();
			}
		
		//TRADUCCION DE POSICION.
		
			//TRADUCE LA POSICION DEL CONTENIDO A LA POSICION DE LA BARRA.
			private function copyMotionToBar():void
			{
				if (direction == "horizontal")
					bar.x = - contenido.x * lane.width / contenido.width + lane.x;
				else
					bar.y = - contenido.y * lane.height / contenido.height + lane.y;
			}
			
			//TRADUCE LA POSICION DE LA BARRA A LA POSICION DEL CONTENIDO.
			private function copyMotionToCont(evt:Event):void
			{
				if (direction == "horizontal")
				{
					contenido.x = contenido.width * (lane.x - bar.x) / lane.width;
					targetPos = contenido.x;
				}
				else
				{
					contenido.y = contenido.height * (lane.y - bar.y) / lane.height;
					targetPos = contenido.y;
				}
			}
			
		//INITBTNS.
		
			//INICIALIZA LOS BOTONES.
			private function initBtns():void
			{	
				//LOS IDENTIFICA.
				prevBtn = formClip.prevBtn;
				nextBtn = formClip.nextBtn;
				
				//LOS TRANSFIERE AL DISPLAYER.
				targetClip.addChild(prevBtn);
				targetClip.addChild(nextBtn);
				
				//ACTIVA LAS MANITOS.
				prevBtn.buttonMode = useHand;
				prevBtn.useHandCursor = useHand;
				nextBtn.buttonMode = useHand;
				nextBtn.useHandCursor = useHand;
				
				//REGISTRA LOS LISTENERS DE LOS BOTONES, Y LES INYECTA ALGUNAS PROPIEDADES PARA MANEJAR LOS EVENTOS.
				prevBtn.dir = -1;
				nextBtn.dir = 1;
				prevBtn.hit.addEventListener(MouseEvent.MOUSE_DOWN, startScroll);
				prevBtn.hit.addEventListener(MouseEvent.MOUSE_UP, endScroll);
				prevBtn.hit.addEventListener(MouseEvent.MOUSE_OUT, endScroll);
				nextBtn.hit.addEventListener(MouseEvent.MOUSE_DOWN, startScroll);
				nextBtn.hit.addEventListener(MouseEvent.MOUSE_UP, endScroll);
				nextBtn.hit.addEventListener(MouseEvent.MOUSE_OUT, endScroll);
				
				//LOS POSICIONA.
				placeBtns();
			}
			
			//POSICIONA LOS BOTONES.
			private function placeBtns():void
			{
				if (direction == "vertical")
				{
					prevBtn.rotation = -180;
					if (vAlign == "right")
					{
						prevBtn.x = box.x + box.width + prevBtn.width/2;
						prevBtn.y = prevBtn.height/2;
						nextBtn.y = box.y + box.height - prevBtn.height/2;
					}
					else if (vAlign == "left")
					{
						prevBtn.x = -prevBtn.width/2;
						prevBtn.y = prevBtn.height/2;
						nextBtn.y = box.y + box.height - prevBtn.height/2;
					}
					nextBtn.x = prevBtn.x;
				}
				else if (direction == "horizontal")
				{
					prevBtn.rotation = 90;
					nextBtn.rotation = -90;
					if (hAlign == "top")
					{
						prevBtn.x = prevBtn.width/2;
						prevBtn.y = -prevBtn.height/2;
						nextBtn.x = box.x + box.width - prevBtn.width/2;
					}
					else if (hAlign == "bottom")
					{
						prevBtn.x = prevBtn.width/2;
						prevBtn.y = box.y + box.height + prevBtn.height/2;
						nextBtn.x = box.x + box.width - prevBtn.width/2;
					}
					nextBtn.y = prevBtn.y;
				}
			}	
			
		//INITBARANDLANE.
		
			//INICIALIZA LA BARRA Y EL LANE.
			private function initBarAndLane():void
			{
				//IDENTIFICA LOS ELEMENTOS.
				bar = formClip.bar;
				lane = formClip.lane;
				
				//TRANSFIERE LOS ELEMENTOS AL DISPLAYER.
				targetClip.addChild(lane);
				targetClip.addChild(bar);
				
				//SETEA EL LANE.
				setLane();
				
				//SETEA LA BARRA.
				setBar();
				
				//DETERMINA EL RECTANGULO DE DRAGGING.
				detDraggingRect();
			}
			
			//SETEA EL LANE.
			private function setLane():void
			{
				//DIMENSIONA.
				if (direction == "vertical")
				{
					lane.x = prevBtn.x - prevBtn.width / 2;
					lane.y = prevBtn.y + prevBtn.height / 2;
					lane.height = nextBtn.y - nextBtn.height / 2 - lane.y;
				}
				else if (direction == "horizontal")
				{
					lane.x = prevBtn.x + prevBtn.width / 2;
					lane.y = prevBtn.y - prevBtn.height / 2;
					lane.width = nextBtn.x - nextBtn.width / 2 - lane.x;
				}
				
				//LISTENER.
				lane.addEventListener(MouseEvent.MOUSE_DOWN, laneClick);
				lane.addEventListener(MouseEvent.MOUSE_UP, endScroll);
			}
			
			//SETEA LA BARRA.
			private function setBar():void
			{
				//MANITO.
				bar.buttonMode = useHand;
				bar.useHandCursor = useHand;
				
				//GENERA UN CLIP INVISIBLE PARA DETECTAR UN MOUSEUP AFUERA DE LA BARRA.
				var barExtra:MovieClip = new MovieClip();
				targetClip.addChildAt(barExtra, 2);
				barExtra.graphics.beginFill(0xFF0000, 0);
				barExtra.graphics.drawRect(0, 0, 2000, lane.height);
				barExtra.graphics.endFill();
				barExtra.x = lane.x;
				barExtra.y = lane.y;
				barExtra.x -= 1000;
				
				//LISTENER PARA DRAGGING.
				bar.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
				bar.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
				barExtra.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
				bar.addEventListener(MouseEvent.MOUSE_UP, endScroll);
				
				//POSICION INICIAL.
				bar.x = lane.x;
				bar.y = lane.y;
				
				//EXTENSION.
				if (direction == "vertical")
				{
					bar.height = lane.height * box.height / contenido.height;
				}
				else if (direction == "horizontal")
				{
					bar.width = lane.width * box.width / contenido.width;
				}
			}
			
			//DETERMINA EL RECTANGULO DE DRAGGING.
			private function detDraggingRect():void
			{
				//REPORTA EL RESULTADO A UN RECTANGULO (NECESARIO PARA EL DRAGGING).
				if (direction == "vertical")
					laneRect = new Rectangle(lane.x, lane.y, 0, lane.height - bar.height);
				else if (direction == "horizontal")
					laneRect = new Rectangle(lane.x, lane.y, lane.width - bar.width, 0);
			}
		
		//JUMPING.
		
			//CLICK EN EL LANE.
			private function laneClick(evt:MouseEvent):void
			{
				//DETERMINA LA DIRECCION.
				var clickPos:Number = direction == "vertical" ? evt.target.mouseY * lane.scaleY : evt.target.mouseX * lane.scaleX;
				scrollDir = direction == "vertical" ? clickPos > bar.y ? 1 : -1 : clickPos > bar.x ? 1 : -1;
				
				//VELOCIDAD TIPO BOTONES.
				currSpeed = laneScrollSpeed;
				
				//IMPULSO INICIAL.
				retarget(scrollImpulse);
				
				//INICIA EL MOVIMIENTO.
				addEventListener(Event.ENTER_FRAME, movimiento);
				
				//INICIA EL CALCULO DE POSICION.
				addEventListener(Event.ENTER_FRAME, calcPos);
			}
			
		//DRAGGING.
		
			//STARTDRAGGING.
			private function startDragging(evt:MouseEvent):void
			{
				//INICIA EL DRAG.
				bar.startDrag(false, laneRect);
				
				//INICIA EL MOVIMIENTO.
				dragMotionCount = 0;
				addEventListener(Event.ENTER_FRAME, dragMotion);
			}
			
			//STOPDRAGGING.
			private function stopDragging(evt:MouseEvent):void
			{
				//CORTA EL DRAG.
				bar.stopDrag();
			}
			
			//DRAGMOTION.
			private function dragMotion(evt:Event):void
			{
				//REPORTA.
				dragMotionCount++;
				
				//REPORTA.
				if (direction == "horizontal")
					targetPos = contenido.width * (lane.x - bar.x) / lane.width;
				else
					targetPos = contenido.height * (lane.y - bar.y) / lane.height;
				
				//CALCULA LA DIFERENCIA.
				var dif:Number = direction == "horizontal" ? targetPos - contenido.x : targetPos - contenido.y;
				
				//MUEVE.
				if (direction == "horizontal")
					contenido.x += motionFactor * dif;
				else
					contenido.y += motionFactor * dif;
				
				//EVALUA EL CORTE DEL MOVIMIENTO.
				if (Math.abs(dif) < 0.1 && dragMotionCount > 10)
				{
					//ANCLEA.
					if (direction == "horizontal")
						contenido.x = targetPos;
					else
						contenido.y = targetPos;
						
					//CORTA.
					removeEventListener(Event.ENTER_FRAME, dragMotion);
				}
			}
	
		//INFO.
		public function info():void
		{
			var str:String = "BarScroller v1.0" + String.fromCharCode(13);
			str += "---------------" + String.fromCharCode(13);
			str += "VARIABLES SETEABLES OBLIGATORIAS:" + String.fromCharCode(13);
			str += "formClip:MovieClip" + String.fromCharCode(13);
			str += "targetClip:MovieClip" + String.fromCharCode(13);
			str += "---------------" + String.fromCharCode(13);
			str += "VARIABLES SETEABLES OPCIONALES:" + String.fromCharCode(13);
			str += "vAlign:String [default = right]" + String.fromCharCode(13);
			str += "hAlign:String [default = bottom]" + String.fromCharCode(13);
			str += "useHand:Boolean [default = true]" + String.fromCharCode(13);
			str += "motionFactor:Number [default = 0.25]" + String.fromCharCode(13);
			str += "scrollSpeed:Number [default = 5]" + String.fromCharCode(13);
			str += "laneScrollSpeed:Number [default = 20]" + String.fromCharCode(13);
			str += "scrollImpulse:Number [default = 5]" + String.fromCharCode(13);
			str += "---------------" + String.fromCharCode(13);
			str += "NOTAS:" + String.fromCharCode(13);
			str += "Jugando con motionFactor se puede usar este mismo scroller tanto para hacer scrollers normales como scrollers acelerados." + String.fromCharCode(13);
			str += "Requiere que se le asocie un clip a scrollear y un clip de forma." + String.fromCharCode(13);
			str += "El clip a scrollear tiene que ser un ContentDisplayer, o sea, tener un contentHolder y un box como minimo." + String.fromCharCode(13);
			str += "---------------";
			trace(str);
		}
	}	
}